package service

import (
	"api/internal/svc"
	"api/internal/types"
	"context"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gomodule/redigo/redis"
	"github.com/google/uuid"
	"github.com/zeromicro/go-zero/core/logx"
)

type CommonService struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCommonService(ctx context.Context, svcCtx *svc.ServiceContext) *CommonService {
	return &CommonService{
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

const (
	SERVEREROR_TEXT = "服务异常"
	SERVEREROR_CODE = 500

	SUCCESS_TEXT = "成功"
	SUCCESS_CODE = 200

	EROOR_400_CODE = 400
	ERROR_400_TEXT = "请求错误"
)

const (
	BAIDU_TTS_TOKEN = "bdTTSToken" // 百度tts token
)

type BaiduTTSToken struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

func ReturnOk() (res *types.ResponseMsg, err error) {
	return &types.ResponseMsg{
		Msg:  SUCCESS_TEXT,
		Code: SUCCESS_CODE,
	}, nil
}

func ReturnError() (res *types.ResponseMsg, err error) {
	return &types.ResponseMsg{
		Msg:  SERVEREROR_TEXT,
		Code: SERVEREROR_CODE,
	}, fmt.Errorf("内部错误")
}

func Return400() (res *types.ResponseMsg, err error) {
	return &types.ResponseMsg{
		Msg:  ERROR_400_TEXT,
		Code: EROOR_400_CODE,
	}, fmt.Errorf("请求错误")
}

// 匹配锁(只加一把锁就行了)
func (commonService *CommonService) MatchLock(conn redis.Conn, lockName string, lockName2 string, acquireTime int, lockTimeout int) (string, error) {
	identifier := uuid.New().String() // 标识
	lockName = "lock:" + lockName
	lockName2 = "lock:" + lockName2
	end := time.Now().Add(time.Duration(acquireTime) * time.Second)
	for time.Now().Before(end) {
		exists, err := commonService.CheckLock(conn, lockName)
		exists2, err2 := commonService.CheckLock(conn, lockName2)
		if err != nil || err2 != nil {
			return "", err
		} else if !exists && !exists2 {
			// 加锁
			if ok, err := commonService.SetNXWithExpire(conn, lockName, identifier, lockTimeout); ok && err == nil {
				return identifier, nil
			}
		}
		logx.Info("等待锁：%s 释放，时间：%d s\n", lockName, int(end.Sub(time.Now()).Seconds()))
		time.Sleep(1 * time.Millisecond)
	}
	return "", fmt.Errorf("未能获取到锁：%s", lockName)
}

// CheckLock 检查锁是否存在
func (commonService *CommonService) CheckLock(conn redis.Conn, lockName string) (bool, error) {
	lockName = "lock:" + lockName
	ttl, err := redis.Int64(conn.Do("TTL", lockName))
	if err != nil {
		return false, err
	}
	switch {
	case ttl > 0:
		return true, nil // 锁存在
	case ttl == -1:
		return true, nil // 永久锁
	case ttl == -2:
		return false, nil // 锁不存在
	default:
		return false, fmt.Errorf("检查锁失败：%s", lockName)
	}
}

// SetNXWithExpire 设置锁并设置过期时间
func (commonService *CommonService) SetNXWithExpire(conn redis.Conn, lockName string, identifier string, lockTimeout int) (bool, error) {
	lockName = "lock:" + lockName
	reply, err := redis.String(conn.Do("SET", lockName, identifier, "NX", "EX", lockTimeout))
	if err != nil {
		return false, err
	}
	return reply == "OK", nil
}

// acquireTime:等待获取时间 取2
// lockTimeout:超时时间 取2
func (commonService *CommonService) Lock(conn redis.Conn, lockName string, acquireTime int, lockTimeout int) (string, error) {
	identifier := uuid.New().String() // 标识
	lockName = "lock:" + lockName
	end := time.Now().Add(time.Duration(acquireTime) * time.Second)
	for time.Now().Before(end) {
		exists, err := commonService.CheckLock(conn, lockName)
		if err != nil {
			return "", err
		} else if !exists {
			// 加锁
			if ok, err := commonService.SetNXWithExpire(conn, lockName, identifier, lockTimeout); ok && err == nil {
				return identifier, nil
			}
		}
		//logx.Info( "等待锁：%s 释放，时间：%d s\n", lockName, int(end.Sub(time.Now()).Seconds()))
		time.Sleep(1 * time.Millisecond)
	}
	return "", fmt.Errorf("未能获取到锁：%s", lockName)
}

// ReleaseLock 释放 Redis 锁
func (commonService *CommonService) ReleaseLock(conn redis.Conn, lockName, identifier string) error {
	// 构建锁的键名
	lockKey := "lock:" + lockName

	// 开启一个事务
	if _, err := conn.Do("MULTI"); err != nil {
		return err
	}

	// 获取当前锁的标识
	if _, err := conn.Do("GET", lockKey); err != nil {
		return err
	}

	// 删除锁
	if _, err := conn.Do("DEL", lockKey); err != nil {
		return err
	}

	// 执行事务
	replies, err := redis.Values(conn.Do("EXEC"))
	if err != nil {
		return err
	}

	// 检查是否成功释放锁
	currentID, _ := redis.String(replies[0], nil)
	if currentID == identifier {
		return nil
	}
	return fmt.Errorf("释放锁失败：%s", lockName)
}

// 调用多吉云的 API
// apiPath：是调用的 API 接口地址，包含 URL 请求参数 QueryString，例如：/console/vfetch/add.json?url=xxx&a=1&b=2
// data：POST 的数据，对象，例如 {a: 1, b: 2}，传递此参数表示不是 GET 请求而是 POST 请求
// jsonMode：数据 data 是否以 JSON 格式请求，默认为 false 则使用表单形式（a=1&b=2）
// 返回值 ret 是一个 map[string]，其中 ret["code"] 为 200 表示 api 请求成功
func (commonService *CommonService) DogeCloudAPI(apiPath string, data map[string]interface{}, jsonMode bool) (ret map[string]interface{}) {
	// 这里替换为你的多吉云永久 AccessKey 和 SecretKey，可在用户中心 - 密钥管理中查看
	// 请勿在客户端暴露 AccessKey 和 SecretKey，那样恶意用户将获得账号完全控制权
	AccessKey := commonService.svcCtx.Config.JixiangYun.AccessKey
	SecretKey := commonService.svcCtx.Config.JixiangYun.SecretKey

	body := ""
	mime := ""
	if jsonMode {
		_body, err := json.Marshal(data)
		if err != nil {
			log.Fatalln(err)
		}
		body = string(_body)
		mime = "application/json"
	} else {
		values := url.Values{}
		for k, v := range data {
			values.Set(k, v.(string))
		}
		body = values.Encode()
		mime = "application/x-www-form-urlencoded"
	}

	signStr := apiPath + "\n" + body
	hmacObj := hmac.New(sha1.New, []byte(SecretKey))
	hmacObj.Write([]byte(signStr))
	sign := hex.EncodeToString(hmacObj.Sum(nil))
	Authorization := "TOKEN " + AccessKey + ":" + sign

	req, err := http.NewRequest("POST", "https://api.dogecloud.com"+apiPath, strings.NewReader(body))
	req.Header.Add("Content-Type", mime)
	req.Header.Add("Authorization", Authorization)
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	} // 网络错误
	defer resp.Body.Close()
	r, err := ioutil.ReadAll(resp.Body)

	json.Unmarshal([]byte(r), &ret)

	// Debug，正式使用时可以注释掉
	fmt.Printf("[DogeCloudAPI] code: %d, msg: %s, data: %s\n", int(ret["code"].(float64)), ret["msg"], ret["data"])
	return
}

func GenerateUniqueID(number int) string {
	uuidObj := uuid.New()
	uuidStr := strings.Replace(uuidObj.String(), "-", "", -1) // 移除连字符
	return uuidStr[:number]                                   // 截取前位数
}

// 保存百度tts token
func (commonService *CommonService) SaveBaiduTTSToken(redis redis.Conn, token *BaiduTTSToken) {
	expireTime := token.ExpiresIn + (time.Now()).Unix() - 60 //  -60s的 防止超时
	token.ExpiresIn = expireTime

	jsonD, _ := json.Marshal(token)
	redis.Do("SET", BAIDU_TTS_TOKEN, jsonD)
}

// 获取百度tts token
func (commonService *CommonService) GetBaiduTTSToken(redis redis.Conn) (token *BaiduTTSToken) {

	ret, _ := redis.Do("GET", BAIDU_TTS_TOKEN)
	if ret != nil {
		json.Unmarshal(ret.([]byte), &token)
		if token.ExpiresIn < (time.Now()).Unix() {
			return token
		}
	}
	// 不存在或者过期 需要重新换token
	src := fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?client_id=%s&client_secret=%s&grant_type=client_credentials", commonService.svcCtx.Config.BaiduTTS.APIKey, commonService.svcCtx.Config.BaiduTTS.SecretKey)
	payload := strings.NewReader(``)
	client := &http.Client{}
	req, err := http.NewRequest("POST", src, payload)

	if err != nil {
		fmt.Println(err)
		return
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")

	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	json.Unmarshal(body, &token)

	// 保存
	commonService.SaveBaiduTTSToken(redis, token)

	return token
}
